iT邦幫忙

2022 iThome 鐵人賽

DAY 18
2

身分驗證與授權在流程上有互相依賴,但它們是兩件不同的任務,所以 Hydra 切分成 Login Provider 與 Consent Provider 兩個服務來處理。使用者執行登入與同意授權後,最終應用程式會拿到 ID Token 與 Access Token。然而,因為登入狀態與授權不可能永久有效,所以這兩個 Token 的時效就需要被管理。

說管理是有點高尚大,事實上就是控制它何時會失效。最直接的,這兩種 Token 本身都有「有效期限」,像 ID token 的有效期限是放在 exp 這個 Claim,而 Access Token 則是存放在資料庫中,可以透過 Token 自我檢查端點取得與 ID Token 相同意義的 exp,或是一開始的 Token 回應有 expires_in 這個欄位了解 Token 多久後失效。

時間到了之後,Token 就會自動失效。但有兩個情境需要主動觸發。一個是使用者請求失效,另一個是非使用者請求失效。使用者請求的情境,比方說使用者不想再分享資訊給應用程式,因此跟授權伺服器請求撤銷 Access Token 權限;非使用者請求的情境,比方說攻擊者已成功竊取到 Access Token,也被系統監控發現,在這個情境底下,則是要將攻擊者的所持有的 Access Token 權限移除。

今天先簡單說明一下 OAuth 2.0 定義的 Access Token 該怎麼處理。OpenID Connect 定義的 ID Token 處理方法比較特別,留到之後再說明。

使用 Token 撤銷端點

撤銷 Token 相關的標準是在 RFC 7009 - OAuth 2.0 Token Revocation 這份文件定義的。

這個標準主要是在定義授權伺服器可以提供撤銷端點,讓應用程式或使用者該可以主動地撤銷授權。比方說,Hydra 預設發放的 Access Token 時效是 3600 秒,在有效的時間內想把 Token 的權限收回,可以呼叫 Hydra SDK 所提供的呼叫撤銷端點方法。把方法和指令整合起來的結果如下::

class OAuth2TokenRevoke extends Command
{
    protected $signature = 'oauth2:token:revoke {token}';

    protected $description = 'Revoke OAuth 2.0 Token by Public API';

    public function handle(PublicApi $public): int
    {
        $token = $this->argument('token');

        $public->revokeOAuth2Token($token);

        $this->line('Revoke done.');

        return 0;
    }
}

Token 的產生方法,除了跑正規的 Authorization Code 流程外,也可以用 Client Credentials 流程(所以昨天才會先寫這個指令)。

另外我們還能用 Token 自我檢查的端點 Introspection Endpoint,來確認 Token 狀態真的有被清除。執行指令的方法如下:

# 產生 Token
❯ php artisan oauth2:token
iamtoken

# 確認 Token 是正常的
❯ php artisan oauth2:token:introspect iamtoken
[
    "active" => true
    ...
]

# 撤銷 Token
❯ php artisan oauth2:token:revoke iamtoken
Revoke done.

# 再次確認 Token 已被撤銷
❯ php artisan oauth2:token:introspect iamtoken
[
    "active" => false
    ...
]

從指令可以看得到已成功撤銷。

探索協定的要求

RFC 7009 協定的範例,跟 SDK 所呈現出來的其實不大一樣,下面先來看協定上的範例:

POST /revoke HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token

從範例可以知道這個 API 需使用 POST 方法,並且需要對應用程式做驗證。其他參數說明如下:

名稱 必要 說明
token REQUIRED 想要撤銷的 Token 字串
token_type_hint OPTIONAL 提示授權伺服器 Token 的類型

這裡可以看到有一個參數是 Hydra 沒有的:token_type_hint。因 Token 的類型有多種類型,如 Access TokenRefresh Token,應用程式可以傳送這個提示給授權伺服器。授權伺服器可以依這個資訊做最佳化搜尋處理。即便依提示找不到,授權伺服器依然有義務要所有 Token 再找一次。也因此,其實授權伺服器可以忽略這個參數,直接對所有的 token 搜尋,或是用使用其他規則來最佳化搜尋,如 GitHub 在 2021 年後調整了 Token 的規格,gho 開頭的是 Access Token,ghr 開頭的是 Refresh Token。

但因為 Hydra SDK 或 API 文件都沒提到這個欄位,因此去翻了一下原始碼,發現其實是有的。然後 Hydra 的最佳化處理方法就是,先找 Access Token 以及先找 Refresh Token 的差別。

回傳的部分,因為就撤銷的目的來說,不管是成功處理或是傳了一個無效的 Token,授權伺服器一律都回傳 200 狀態碼。錯誤則是只有一個 unsupported_token_type 定義,它代表授權服務無法處理該 Token 類型,比方說授權伺服器不支援撤銷 Refresh Token。然而,Hydra 的 Token 都是支援可以撤銷的,因此沒有這個情境。

今天所完成的程式可以參考 GitHub Commit


上一篇
實作 Client Credentials 授權
下一篇
由應用程式啟動登出流程
系列文
30 天與九頭蛇先生做好朋友23
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言